﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Forms;
using BrightIdeasSoftware;
using DBCodeBuilder.Core.Classes;
using DBCodeBuilder.Core.Enum;
using DBCodeBuilder.Core.Interfaces;
using DBCodeBuilder.Core.Models;
using HSystem.Data;

namespace DBCodeBuilder.UI
{
    public partial class ObjectSelector_Designer : HComp
    {
        private const string ServiceName = "app";
        public event EventHandler OnSchemaChanged;

        #region Properties

        public string SelectedOwner
        {
            get { return cmBoxSchema.SelectedValue == null ? "" : cmBoxSchema.SelectedValue.ToString(); }
        }

        public string SelectedObjectType
        {
            get { return cmBoxObjectType.SelectedValue == null ? "" : cmBoxObjectType.SelectedValue.ToString(); }
        }

        public List<TableModel> tables { get; set; }

        #endregion

        public ObjectSelector_Designer()
        {
            InitializeComponent();

            if (IsDesignMode)
                return;

            clmImage.AspectGetter = x => ((IDbObjectModel) x).ObjectType;
            clmImage.AspectToStringConverter = delegate { return String.Empty; };
            clmImage.ImageGetter = x =>
            {
                switch (((IDbObjectModel) x).ObjectType)
                {
                    case DbObjectType.View:
                        return 1;
                    case DbObjectType.Table:
                        return 0;
                    default:
                        throw new Exception("Object unrecognized");
                }
            };
            clmImage.TextAlign = HorizontalAlignment.Center;

            var dtt = DbCustom.GetTable("SELECT * FROM ALL_USERS ORDER BY USERNAME ASC", ServiceName);
            var emptyRow = dtt.NewRow();
            emptyRow["USERNAME"] = "";
            dtt.Rows.InsertAt(emptyRow, 0);

            cmBoxSchema.DisplayMember = "USERNAME";
            cmBoxSchema.ValueMember = "USERNAME";
            cmBoxSchema.DataSource = dtt;
        }

        #region Events

        private void cmBoxSchema_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmBoxSchema.SelectedIndex == 0)
            {
                cmBoxObjectType.DataSource = null;
                olvItems.ClearObjects();
                return;
            }

            LoadObjectTypeCombo();

            if (OnSchemaChanged != null)
                OnSchemaChanged(null, null);
        }

        private void LoadObjectTypeCombo()
        {
            var itemsObjType = new[]
            {
                new {Text = "", Value = ""},
                new {Text = "Tables", Value = "Tables"},
                new {Text = "Views", Value = "Views"},
                new {Text = "All", Value = "All"}
            };

            cmBoxObjectType.DisplayMember = "Text";
            cmBoxObjectType.ValueMember = "Value";
            cmBoxObjectType.DataSource = itemsObjType;
        }

        private void cmBoxObjectType_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (cmBoxObjectType.SelectedIndex == 0)
            {
                olvItems.ClearObjects();
                txtFiltro.Enabled = false;
                txtFiltro.Text = "";
                return;
            }
            txtFiltro.Enabled = true;
            RefreshObjects();
        }

        private void txtFiltro_TextChanged(object sender, EventArgs e)
        {
            TextMatchFilter filter = TextMatchFilter.Contains(olvItems, txtFiltro.Text);
            olvItems.ModelFilter = filter;
            olvItems.DefaultRenderer = new HighlightTextRenderer(filter);
        }

        #endregion

        #region Search

        private List<IDbObjectModel> SearchAllObjects()
        {
            return GetObjects(ObjectView.All);
        }

        private List<IDbObjectModel> SearchObjects()
        {
            string toCheck = cmBoxObjectType.Text;
            List<IDbObjectModel> objs;
            switch (toCheck)
            {
                case "Tables":
                    objs = GetObjects(ObjectView.Tables);
                    break;
                case "Views":
                    objs = GetObjects(ObjectView.Views);
                    break;
                case "All":
                    objs = GetObjects(ObjectView.All);
                    break;
                default:
                    return new List<IDbObjectModel>();
            }
            return objs;
        }

        private List<IDbObjectModel> GetObjects(ObjectView objectView)
        {
            List<IDbObjectModel> res = new List<IDbObjectModel>();
            string owner = cmBoxSchema.SelectedValue.ToString();

            switch (objectView)
            {
                case ObjectView.Tables:
                    res.AddRange(GetTables(owner));
                    break;

                case ObjectView.Views:
                    res.AddRange(Getviews(owner));
                    break;

                case ObjectView.All:
                    res.AddRange(GetTables(owner));
                    res.AddRange(Getviews(owner));
                    break;
            }
            return res;
        }

        private static IEnumerable<TableModel> GetTables(string owner)
        {
            List<string> tmpList = DbMetadata.GetTableListBySchema(owner, ServiceName);
            return tmpList.Select(name => new TableModel {Name = name, Owner = owner}).ToList();
        }

        private static IEnumerable<ViewModel> Getviews(string owner)
        {
            List<string> tmpList = DbMetadata.GetViewListBySchema(owner, ServiceName);
            return tmpList.Select(name => new ViewModel {Name = name, Owner = owner}).ToList();
        }

        #endregion

        #region Cleanup

        public void RemoveObjects(List<IDbObjectModel> obj)
        {
            olvItems.RemoveObjects(obj);
        }

        public void RemoveObject(IDbObjectModel obj)
        {
            olvItems.RemoveObject(obj);
        }

        private List<string> CleanupObjects(List<IDbObjectModel> allDbObjs)
        {
            string toCheck = cmBoxObjectType.Text;
            var anomalie = new List<string>();
            switch (toCheck)
            {
                case "Tables":
                    var tabs = tables.Where(t => t.ObjectType == DbObjectType.Table).ToList();
                    CleanObjectList(allDbObjs, tabs.Cast<IDbObjectModel>().ToList(), anomalie);
                    break;
                case "Views":
                    var views = tables.Where(t => t.ObjectType == DbObjectType.View).ToList();
                    CleanObjectList(allDbObjs, views.Cast<IDbObjectModel>().ToList(), anomalie);
                    break;
                case "All":
                    CleanObjectList(allDbObjs, tables.Cast<IDbObjectModel>().ToList(), anomalie);
                    break;
            }
            return anomalie;
        }

        private void CleanObjectList(List<IDbObjectModel> allDbObjs, List<IDbObjectModel> toClean, List<string> anomalie)
        {
            foreach (var model in toClean)
            {
                if (TryRemoveObject(allDbObjs, model, anomalie))
                    continue;

                if (model.GetType() == typeof (TableModel))
                    tables.Remove(tables.Single(t => t.Name == model.Name));
            }
        }

        private static bool TryRemoveObject(List<IDbObjectModel> allDbObjs, IDbObjectModel table, List<string> ret)
        {
            if (allDbObjs.SingleOrDefault(t => t.Name == table.Name) == null)
            {
                ret.Add(table.Owner + "." + table.Name);
                return false;
            }

            allDbObjs.Remove(allDbObjs.Single(t => t.Name == table.Name));
            return true;
        }

        #endregion

        #region Caricamento/Inizializzazione

        internal bool LoadByConfig(ConfigSetup configs, out List<string> exceptions)
        {
            exceptions = new List<string>();
            Reset();

            if (String.IsNullOrEmpty(configs.Owner))
                return true;

            cmBoxSchema.SelectedIndexChanged -= cmBoxSchema_SelectedIndexChanged;
            cmBoxSchema.SelectedValue = configs.Owner;
            if (cmBoxSchema.SelectedIndex.In(0, -1))
            {
                MessageBox.Show("Loaded configuration references a non-existent schema!");
                return false;
            }
            cmBoxSchema.SelectedIndexChanged += cmBoxSchema_SelectedIndexChanged;

            LoadObjectTypeCombo();
            if (String.IsNullOrEmpty(configs.SelectedObjectType))
                return true;

            cmBoxObjectType.SelectedIndexChanged -= cmBoxObjectType_SelectedIndexChanged;
            cmBoxObjectType.SelectedValue = configs.SelectedObjectType;
            cmBoxObjectType.SelectedIndexChanged += cmBoxObjectType_SelectedIndexChanged;
            txtFiltro.Enabled = true;

            List<IDbObjectModel> objs = SearchAllObjects();
            exceptions = CleanupObjects(objs);

            RefreshObjects();
            return true;
        }

        internal void RefreshObjects()
        {
            var objs = SearchObjects();
            CleanupObjects(objs);
            olvItems.Objects = objs;
        }

        public void Reset()
        {
            cmBoxSchema.SelectedIndex = 0;
            txtFiltro.Enabled = false;
            olvItems.ClearObjects();
        }

        internal void SelectObjectsByName(List<string> list)
        {
            olvItems.SelectObjects(olvItems.Objects.Cast<IDbObjectModel>().Where(t => list.Contains(t.Name)).ToList());
        }
        #endregion
    }
}